﻿using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using EasyMapping.Common.Utility;
using System;


namespace EasyMapping.Core
{
    class XMLModelConfigLoader: IModelConfigLoader
    {
        #region " Simple Singleton "

        public static  XMLModelConfigLoader Instance = new XMLModelConfigLoader();
        private XMLModelConfigLoader()
        {

        }
 
        #endregion

        #region IModelConfigLoader Members

        public bool TryLoadData<T>(string procedureName,  ParameterProperty[] arr,out IStoreProcedure sp)
        {
            string modelName = typeof(T).Name;
            XmlNode procedure = GetProcedureNode(modelName, procedureName);
            IDataProvider provider = DataProviderFactory.GetDataProvider();

            sp = provider.CreateProcedure();
            ParameterPropertyArray paras = new ParameterPropertyArray(arr);

            if (procedure != null)
            {
                sp.Name = modelName + "." + procedureName;
                sp.ParaMap = provider.CreateProcedureParameterList();
                XmlNode parasNode = GetParametersNode(procedure);

                if (parasNode != null)
                {
                    foreach (XmlNode item in parasNode.ChildNodes)
                    {
                        if (item.Name.Equals(Constants.Model.StoreProcedureParameterName))
                        {
                            IProcedureParameter parameter = provider.CreateProcedureParameter();
                            parameter.name = item.Attributes[Constants.Model.NodeIdentifierName].Value;
                            SetParameterValue(parameter, provider, paras, item);
                            sp.ParaMap.Add(parameter.name, parameter);
                        }
                    }
                }
                return true;
            }
            return false;
        }

        public bool TryLoadData<T>(string procedureName, T obj, out IStoreProcedure sp)
        {
            Type type = typeof(T);
            string modelName = type.Name;
            XmlNode procedure = GetProcedureNode(modelName, procedureName);
            IDataProvider provider = DataProviderFactory.GetDataProvider();

            sp = provider.CreateProcedure();
            if (procedure != null)
            {
                sp.Name = modelName + "." + procedureName;
                sp.ParaMap = provider.CreateProcedureParameterList();
                XmlNode parasNode = GetParametersNode(procedure);

                if (parasNode != null)
                {
                    foreach (XmlNode item in parasNode.ChildNodes)
                    {
                        if (item.Name.Equals(Constants.Model.StoreProcedureParameterName))
                        {
                            IProcedureParameter parameter = provider.CreateProcedureParameter();
                            parameter.name = item.Attributes[Constants.Model.NodeIdentifierName].Value;
                            SetParameterValue(parameter, provider, obj, item);
                            sp.ParaMap.Add(parameter.name, parameter);                           
                        }
                    }
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region " Non-Public Methods "

        static private XmlNode GetProcedureNode( string modelName, string procedureName)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase) + Constants.Model.ConfigPath + modelName + ".xml");
            string procedureCondition = string.Format(Constants.Model.SelectProcedureCondition, modelName, procedureName);
            XmlNode model = doc.SelectSingleNode(procedureCondition);
            return model;
        }

        static private XmlNode GetParametersNode(XmlNode procedure)
        {
             XmlNode node = default(XmlNode);
             if (procedure != null)
             {
                 foreach (XmlNode item in procedure.ChildNodes)
                 {
                     if (item.Name.Equals(Constants.Model.StoreProcedureParametersName, System.StringComparison.OrdinalIgnoreCase))
                     {
                         node = item;
                         break;
                     }
                 }
             }
            return node;
        }

        static private void SetParameterValue(IProcedureParameter para, IDataProvider provider,ParameterPropertyArray arr ,XmlNode item)
        {
            if (provider is SQLDataProvider)
            {
                SQLProcedureParameterWrapper temp = para as SQLProcedureParameterWrapper;
                temp.type = TypeConverter.GetSqlDbType(item.Attributes[Constants.Model.StoreProcedureParameterTypeName].Value);
                XmlAttribute pair = item.Attributes[Constants.Model.StoreProcedureParameterOutputName];
                temp.objectColumn = (pair != null) ? pair.Value.ToString() : null;
                if (temp.name != null
                    && arr[temp.name] != null)
                {
                    temp.value = arr[temp.name].Value;
                }

                XmlAttribute useOutput = item.Attributes[Constants.Model.StoreProcedureParameterOutputName];
                if (useOutput != null)
                {
                    temp.IsOutput = bool.Parse(useOutput.Value.ToString());
                }
            }
        }

        static private void SetParameterValue(IProcedureParameter para, IDataProvider provider, object obj, XmlNode item)
        {
            if (provider is SQLDataProvider)
            {
                SQLProcedureParameterWrapper temp = para as SQLProcedureParameterWrapper;
                temp.type = TypeConverter.GetSqlDbType(item.Attributes[Constants.Model.StoreProcedureParameterTypeName].Value);
                XmlAttribute pair = item.Attributes[Constants.Model.StoreProcedureParameterObjectColumnName];
                temp.objectColumn = (pair != null) ? pair.Value.ToString() : null;
                if ( temp.objectColumn != null)
                {
                    temp.propertyInfo = obj.GetType().GetProperty(temp.objectColumn);
                    temp.value = temp.propertyInfo.GetValue(obj, null);
                }

                XmlAttribute useOutput = item.Attributes[Constants.Model.StoreProcedureParameterOutputName];
                if (useOutput != null)
                {
                    temp.IsOutput = bool.Parse(useOutput.Value.ToString());
                }
            }        
        }

        #endregion

    }
}
